package com.hmall.search.controller;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmall.api.dto.ItemDTO;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.utils.CollUtils;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Api(tags = "搜索相关接口")
@RestController
@RequestMapping("/search")
@RequiredArgsConstructor
public class SearchController {

    private final RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
                    HttpHost.create("http://192.168.116.103:9200"))
    );
    @ApiOperation("搜索商品")
    @GetMapping("/list")
    public PageDTO<ItemDTO> search(ItemPageQuery query) throws IOException {
        SearchRequest request = new SearchRequest("items");
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(query.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
        }
        if (StrUtil.isNotBlank(query.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", query.getBrand()));
        }
        if (StrUtil.isNotBlank(query.getCategory())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", query.getCategory()));
        }
        if (query.getMinPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        }
        if (query.getMaxPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        }
        //条件构造
        request.source().query(boolQueryBuilder);
        //分页
        request.source().from(query.getPageNo()).size(query.getPageSize());

        //排序
        if (StrUtil.isNotBlank(query.getSortBy())) {
            request.source().sort(query.getSortBy(), query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        } else {
            request.source().sort("updateTime", query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        }
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        PageDTO<ItemDTO> itemDTOPageDTO = parseResponseResult(response, query);
        this.client.close();
        return itemDTOPageDTO;
    }
    private PageDTO<ItemDTO> parseResponseResult(SearchResponse response,ItemPageQuery query){
        List<ItemDoc> itemDocList = new ArrayList<>();
        SearchHits searchHits = response.getHits();
        // 4.1、获取总记录数
        long total = searchHits.getTotalHits().value;
        System.out.println("共搜索到" + total + "条数据");
        // 4.2、获取文档数组
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            // 3.得到_source，也就是原始json文档
            String source = hit.getSourceAsString();
            // 4.反序列化
            ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
            // 5.获取高亮结果
            Map<String, HighlightField> hfs = hit.getHighlightFields();
            if (CollUtils.isNotEmpty(hfs)) {
                // 5.1.有高亮结果，获取name的高亮结果
                HighlightField hf = hfs.get("name");
                if (hf != null) {
                    // 5.2.获取第一个高亮结果片段，就是商品名称的高亮值
                    String hfName = hf.getFragments()[0].string();
                    item.setName(hfName);
                }
            }
            itemDocList.add(item);
        }
        // 转为ItemDTO集合
        List<ItemDTO> itemDTOList = BeanUtil.copyToList(itemDocList, ItemDTO.class);
        // 页面大小
        long pageSize = query.getPageSize().longValue();
        // 返回结果
        PageDTO<ItemDTO> itemDTOPageDTO = new PageDTO<>(total,pageSize,itemDTOList);

        return itemDTOPageDTO;
    }
    @ApiOperation("搜索商品分类")
    @PostMapping("/filters")
    public Map filters(ItemPageQuery query) throws IOException {
        SearchRequest request = new SearchRequest("items");
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(query.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
        }
        if (StrUtil.isNotBlank(query.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", query.getBrand()));
        }
        if (StrUtil.isNotBlank(query.getCategory())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", query.getCategory()));
        }
        if (query.getMinPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
        }
        if (query.getMaxPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
        }
        //设置查询和分页
        request.source().query(boolQueryBuilder).size(0);
        //设置聚合条件
        String brandAggName = "brandAgg";
        String categoryAggName = "categoryAgg";
        request.source().aggregation(AggregationBuilders.terms(brandAggName).field("brand"))
                .aggregation(AggregationBuilders.terms(categoryAggName).field("category"));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析结果
        Aggregations aggregations = response.getAggregations();
        Terms brandTerms = aggregations.get(brandAggName);
        Terms categoryTerms = aggregations.get(categoryAggName);
        List<? extends Terms.Bucket> brandBuckets = brandTerms.getBuckets();
        List<? extends Terms.Bucket> categoryBuckets = categoryTerms.getBuckets();
        //定义返回结果
        HashMap<String, List<String>> resultMap = new HashMap<>();
        Terms terms = aggregations.get(brandAggName);
        if(terms != null){
            resultMap.put("brand", terms.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
        }
        terms = aggregations.get(categoryAggName);
        if(terms != null){
            resultMap.put("category", terms.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
        }
        return resultMap;
    }
}
